#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Systemd权限维持模块单元测试
"""

import os
import sys
import unittest
import tempfile
import subprocess
from pathlib import Path
from unittest.mock import patch, MagicMock

# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent.parent.parent
sys.path.insert(0, str(project_root))

from modules.persistence.systemd.core import SystemdPersistenceModule
from modules.persistence.base import PersistenceConfig, PersistenceStatus


class TestSystemdPersistenceModule(unittest.TestCase):
    """Systemd权限维持模块测试类"""
    
    def setUp(self):
        """测试初始化"""
        self.module = SystemdPersistenceModule()
        self.test_payload = "echo 'test payload' > /tmp/test.log"
        self.test_config = PersistenceConfig(
            payload=self.test_payload,
            stealth_level=5
        )
    
    def tearDown(self):
        """测试清理"""
        # 清理所有测试实例
        try:
            self.module.cleanup_all_instances()
        except:
            pass
    
    def test_module_initialization(self):
        """测试模块初始化"""
        self.assertEqual(self.module.module_name, "systemd")
        self.assertEqual(self.module.module_version, "1.0.0")
        self.assertIn("linux", self.module.supported_platforms)
        self.assertIsNotNone(self.module.config)
    
    def test_config_validation(self):
        """测试配置验证"""
        # 有效配置
        valid_config = PersistenceConfig(
            payload="echo test",
            stealth_level=5
        )
        result = self.module.validate_config(valid_config)
        self.assertTrue(result.success)
        
        # 无效配置 - 空payload
        invalid_config = PersistenceConfig(
            payload="",
            stealth_level=5
        )
        result = self.module.validate_config(invalid_config)
        self.assertFalse(result.success)
        
        # 无效配置 - 错误的stealth_level
        invalid_config = PersistenceConfig(
            payload="echo test",
            stealth_level=15
        )
        result = self.module.validate_config(invalid_config)
        self.assertFalse(result.success)
    
    def test_service_name_generation(self):
        """测试服务名称生成"""
        # 低隐蔽性
        name1 = self.module._generate_service_name(3)
        self.assertTrue(name1.startswith("system-service-"))
        
        # 高隐蔽性
        name2 = self.module._generate_service_name(8)
        self.assertIn("-", name2)
        self.assertNotEqual(name1, name2)
    
    def test_service_location_selection(self):
        """测试服务位置选择"""
        # 低隐蔽性 - 系统级服务
        location1 = self.module._select_service_location(3)
        self.assertEqual(location1, "/etc/systemd/system/")
        
        # 高隐蔽性 - 用户级服务
        location2 = self.module._select_service_location(8)
        self.assertTrue(location2.endswith("/.config/systemd/user/"))
    
    def test_service_content_creation(self):
        """测试服务文件内容创建"""
        service_name = "test-service"
        content = self.module._create_service_content(self.test_config, service_name)
        
        # 检查必要的section
        self.assertIn("[Unit]", content)
        self.assertIn("[Service]", content)
        self.assertIn("[Install]", content)
        
        # 检查关键配置
        self.assertIn("Description=", content)
        self.assertIn("ExecStart=", content)
        self.assertIn("Restart=always", content)
        self.assertIn("WantedBy=multi-user.target", content)
    
    def test_payload_encoding(self):
        """测试payload编码"""
        # 低隐蔽性
        encoded1 = self.module._encode_service_payload(self.test_payload, 3)
        self.assertEqual(encoded1, self.test_payload)
        
        # 中等隐蔽性
        encoded2 = self.module._encode_service_payload(self.test_payload, 6)
        self.assertTrue(encoded2.startswith("/bin/bash -c"))
        
        # 高隐蔽性
        encoded3 = self.module._encode_service_payload(self.test_payload, 9)
        self.assertTrue(encoded3.startswith("/bin/bash"))
        self.assertIn("/tmp/", encoded3)
    
    @patch('subprocess.run')
    @patch('os.makedirs')
    @patch('builtins.open')
    @patch('os.chmod')
    def test_deploy_success(self, mock_chmod, mock_open, mock_makedirs, mock_subprocess):
        """测试成功部署"""
        # Mock subprocess调用
        mock_subprocess.return_value = MagicMock(returncode=0)
        
        # Mock文件操作
        mock_file = MagicMock()
        mock_open.return_value.__enter__.return_value = mock_file
        
        # 执行部署
        result = self.module.deploy(self.test_config)
        
        # 验证结果
        self.assertTrue(result.success)
        self.assertIsNotNone(result.instance_id)
        self.assertIn("service_name", result.details)
        
        # 验证调用
        mock_makedirs.assert_called()
        mock_open.assert_called()
        mock_chmod.assert_called()
        self.assertEqual(mock_subprocess.call_count, 3)  # daemon-reload, enable, start
    
    @patch('subprocess.run')
    def test_check_alive(self, mock_subprocess):
        """测试状态检查"""
        # 创建测试实例配置
        instance_id = "test-instance"
        instance_config = {
            'service_name': 'test-service',
            'service_file': '/etc/systemd/system/test-service.service',
            'payload': self.test_payload,
            'stealth_level': 5
        }
        self.module.save_instance_config(instance_id, instance_config)
        
        # Mock systemctl调用 - 服务活跃
        mock_subprocess.side_effect = [
            MagicMock(stdout="active\n", returncode=0),  # is-active
            MagicMock(stdout="enabled\n", returncode=0)  # is-enabled
        ]
        
        # Mock文件存在
        with patch('os.path.exists', return_value=True):
            result = self.module.check_alive(instance_id)
        
        # 验证结果
        self.assertTrue(result.success)
        self.assertTrue(result.details['is_active'])
        self.assertTrue(result.details['is_enabled'])
        self.assertTrue(result.details['file_exists'])
    
    @patch('subprocess.run')
    @patch('os.path.exists')
    @patch('builtins.open')
    @patch('os.chmod')
    def test_repair(self, mock_chmod, mock_open, mock_exists, mock_subprocess):
        """测试修复功能"""
        # 创建测试实例配置
        instance_id = "test-instance"
        instance_config = {
            'service_name': 'test-service',
            'service_file': '/etc/systemd/system/test-service.service',
            'payload': self.test_payload,
            'stealth_level': 5
        }
        self.module.save_instance_config(instance_id, instance_config)
        
        # Mock文件不存在，需要重新创建
        mock_exists.return_value = False
        
        # Mock subprocess调用
        mock_subprocess.side_effect = [
            MagicMock(returncode=0),  # daemon-reload
            MagicMock(stdout="disabled\n", returncode=0),  # is-enabled
            MagicMock(returncode=0),  # enable
            MagicMock(stdout="inactive\n", returncode=0),  # is-active
            MagicMock(returncode=0)   # start
        ]
        
        # Mock文件操作
        mock_file = MagicMock()
        mock_open.return_value.__enter__.return_value = mock_file
        
        # 执行修复
        result = self.module.repair(instance_id)
        
        # 验证结果
        self.assertTrue(result.success)
        self.assertIn("repair_actions", result.details)
        self.assertGreater(len(result.details["repair_actions"]), 0)
    
    @patch('subprocess.run')
    @patch('os.path.exists')
    @patch('os.remove')
    def test_clean(self, mock_remove, mock_exists, mock_subprocess):
        """测试清理功能"""
        # 创建测试实例配置
        instance_id = "test-instance"
        instance_config = {
            'service_name': 'test-service',
            'service_file': '/etc/systemd/system/test-service.service',
            'payload': self.test_payload,
            'stealth_level': 5
        }
        self.module.save_instance_config(instance_id, instance_config)
        
        # Mock文件存在
        mock_exists.return_value = True
        
        # Mock subprocess调用
        mock_subprocess.return_value = MagicMock(returncode=0)
        
        # 执行清理
        result = self.module.clean(instance_id)
        
        # 验证结果
        self.assertTrue(result.success)
        self.assertIn("cleanup_actions", result.details)
        
        # 验证调用
        mock_remove.assert_called()
        self.assertGreaterEqual(mock_subprocess.call_count, 3)  # stop, disable, daemon-reload
    
    def test_list_instances(self):
        """测试实例列表"""
        # 创建测试实例配置
        instance_id1 = "test-instance-1"
        instance_config1 = {
            'service_name': 'test-service-1',
            'service_file': '/etc/systemd/system/test-service-1.service',
            'stealth_level': 5,
            'created_time': '2024-01-01T00:00:00'
        }
        self.module.save_instance_config(instance_id1, instance_config1)
        
        instance_id2 = "test-instance-2"
        instance_config2 = {
            'service_name': 'test-service-2',
            'service_file': '/etc/systemd/system/test-service-2.service',
            'stealth_level': 7,
            'created_time': '2024-01-02T00:00:00'
        }
        self.module.save_instance_config(instance_id2, instance_config2)
        
        # Mock check_alive方法
        with patch.object(self.module, 'check_alive') as mock_check:
            mock_check.return_value = MagicMock(
                success=True,
                details={'status': 'active', 'is_active': True}
            )
            
            instances = self.module.list_instances()
        
        # 验证结果
        self.assertEqual(len(instances), 2)
        self.assertEqual(instances[0]['instance_id'], instance_id1)
        self.assertEqual(instances[1]['instance_id'], instance_id2)
    
    def test_platform_support(self):
        """测试平台支持检查"""
        # 在Windows上应该返回False
        with patch('os.name', 'nt'):
            self.assertFalse(self.module.is_platform_supported())
        
        # 在Linux上应该返回True
        with patch('os.name', 'posix'):
            self.assertTrue(self.module.is_platform_supported())
    
    def test_module_info(self):
        """测试模块信息"""
        info = self.module.get_module_info()
        
        self.assertEqual(info['name'], 'systemd')
        self.assertEqual(info['version'], '1.0.0')
        self.assertIn('linux', info['supported_platforms'])
        self.assertIn('description', info)


class TestSystemdModuleIntegration(unittest.TestCase):
    """Systemd模块集成测试"""
    
    def setUp(self):
        """集成测试初始化"""
        self.module = SystemdPersistenceModule()
    
    @unittest.skipUnless(os.name == 'posix', "需要Linux系统")
    def test_systemd_availability(self):
        """测试systemd可用性"""
        try:
            result = subprocess.run(['which', 'systemctl'], 
                                  capture_output=True, text=True)
            systemctl_available = result.returncode == 0
            
            if systemctl_available:
                result = subprocess.run(['systemctl', '--version'], 
                                      capture_output=True, text=True)
                systemd_working = result.returncode == 0
                
                self.assertTrue(systemd_working, "systemd应该可用")
            else:
                self.skipTest("systemctl命令不可用")
                
        except Exception as e:
            self.skipTest(f"无法检查systemd可用性: {e}")
    
    @unittest.skipUnless(os.name == 'posix', "需要Linux系统")
    def test_service_file_creation(self):
        """测试服务文件创建（不实际部署）"""
        config = PersistenceConfig(
            payload="echo 'integration test'",
            stealth_level=3
        )
        
        service_name = "test-integration-service"
        content = self.module._create_service_content(config, service_name)
        
        # 验证生成的服务文件内容
        self.assertIn("[Unit]", content)
        self.assertIn("[Service]", content)
        self.assertIn("[Install]", content)
        self.assertIn("echo 'integration test'", content)


def run_tests():
    """运行所有测试"""
    # 创建测试套件
    loader = unittest.TestLoader()
    suite = unittest.TestSuite()
    
    # 添加测试类
    suite.addTests(loader.loadTestsFromTestCase(TestSystemdPersistenceModule))
    suite.addTests(loader.loadTestsFromTestCase(TestSystemdModuleIntegration))
    
    # 运行测试
    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(suite)
    
    return result.wasSuccessful()


if __name__ == "__main__":
    print("=== Systemd权限维持模块单元测试 ===")
    
    # 检查测试环境
    print(f"Python版本: {sys.version}")
    print(f"操作系统: {os.name}")
    print(f"当前目录: {os.getcwd()}")
    
    # 运行测试
    success = run_tests()
    
    if success:
        print("\n✓ 所有测试通过")
        sys.exit(0)
    else:
        print("\n✗ 部分测试失败")
        sys.exit(1)